package com.app.util.db.tool;

import java.lang.reflect.Method;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import com.app.util.db.DBPool;
import com.app.util.db.bean.GroupBy;
import com.app.util.db.bean.Limit;
import com.app.util.db.bean.OrderBy;
import com.app.util.db.bean.ResultList;
import com.app.util.db.bean.ResultMap;
import com.app.util.db.bean.TableDefined;
import com.app.util.db.util.SqlFieldUtil;
import com.app.util.db.util.SqlNameTranser;
import com.app.util.db.util.TableDefinedUtil;
import com.app.util.db.where.Expression;
import com.app.util.pool.NewObjectException;

public class Select extends SQLShell {

	public Select(DBPool dbPool) {
		super(dbPool);
		this.mode = 1;
	}

	public Select(DBPool dbPool, int mode) {
		super(dbPool);
		this.mode = mode;
	}
 
	private int mode;

	public static final int MODE_DEFAULT = 1; // 默认
	public static final int MODE_FIELDS_FROM_CLASS = 2; // 使用对象类的属性作为字段名
	public static final int MODE_IGNORE_PARSE_ERROR = 3; // 忽略错误

	private boolean bigInt2Long;

	public boolean isBigInt2Long() {
		return bigInt2Long;
	}

	public void setBigInt2Long(boolean bigInt2Long) {
		this.bigInt2Long = bigInt2Long;
	}

	/**
	 * 将结果集封装成LinkedList[Bean]的集合 <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param rl
	 *            结果集
	 * @return LinkedList<Bean>
	 */
	public <T> LinkedList<T> parseList(Class<T> c, ResultList rl) {
		LinkedList<T> list = new LinkedList<T>();
		for (ResultMap rm : rl) {
			list.add(parseObject(c, rm));
		}
		return list;
	}

	/**
	 * 将结果行数据封装成Bean对像 <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param rm
	 *            结果行数据
	 * @return Bean
	 */
	public <T> T parseObject(Class<T> c, ResultMap rm) {
		if (null == rm) {
			return null;
		}
		T obj;
		try {
			obj = c.newInstance();
		} catch (Exception e) {
			throw new RuntimeException("ResultMap parse to Object error.", e);
		}
		for (Entry<String, ?> ent : rm.entrySet()) {
			Object value = ent.getValue();
			if (null == value) {
				continue;
			}
			String fieldName = ent.getKey();
			String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
			try {
				Class<?> vClass = value.getClass();
				if (value instanceof java.sql.Timestamp || value instanceof java.sql.Date) {
					vClass = Date.class;
				}
				if (bigInt2Long && value instanceof BigInteger) {
					vClass = Long.class;
					value = ((BigInteger) value).longValue();
				}
				Method setMethod = c.getMethod(setMethodName, vClass);
				setMethod.invoke(obj, value);
			} catch (Exception e) {
				if (mode != MODE_IGNORE_PARSE_ERROR) {
					throw new RuntimeException("ResultMap parse to Object error.", e);
				}
			}
		}
		return obj;
	}

	/**
	 * 返回Bean的集合，自动将查询出的数据列表转换成对象集合 <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param sql
	 *            查询语句
	 * @return Bean的集合
	 * @throws SQLException
	 */
	public <T> List<T> select(Class<T> c, String sql) throws SQLException {
		return parseList(c, select(sql));
	}

	/**
	 * 返回Bean的集合，自动将查询出的数据列表转换成对象集合 <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param sql
	 *            查询语句
	 * @param params
	 *            查询参数
	 * @return Bean的集合
	 * @throws SQLException
	 */
	public <T> List<T> select(Class<T> c, String sql, Object[] params) throws SQLException {
		return parseList(c, select(sql, params));
	}

	/**
	 * 返回一个Bean，自动将查询到的单个数据对象转换成Bean <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param sql
	 *            查询语句
	 * @return
	 * @throws SQLException
	 */
	public <T> T selectSingle(Class<T> c, String sql) throws SQLException {
		return parseObject(c, selectSingle(sql));
	}

	/**
	 * 返回一个Bean，自动将查询到的单个数据对象转换成Bean <br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高编辑速度）
	 * 
	 * @param c
	 *            Bean的类型
	 * @param sql
	 *            查询语句
	 * @param params
	 *            查询参数
	 * @return Bean的集合
	 * @throws SQLException
	 */
	public <T> T selectSingle(Class<T> c, String sql, Object[] params) throws SQLException {
		return parseObject(c, selectSingle(sql, params));
	}

	/**
	 * 通过ID得到一条记录
	 * 
	 * @param c
	 *            Bean类型
	 * @param value
	 *            ID对应的值
	 * @return
	 * @throws SQLException
	 */
	public <T> T selectSingle(Class<T> c, Object value) throws SQLException {
		TableDefined tableDefined = TableDefinedUtil.ask(c);
		return selectSingle(c, new Expression(tableDefined.id()).eq(value));
	}

	/**
	 * 返回一个Bean，自动将查询到的单个数据对象转换成Bean
	 * 
	 * @param c
	 *            Bean类型
	 * @param where
	 *            条件表达式
	 * @return
	 * @throws SQLException
	 */
	public <T> T selectSingle(Class<T> c, Expression where) throws SQLException {
		return selectSingle(c, where, null);
	}

	/**
	 * 返回一个Bean，自动将查询到的单个数据对象转换成Bean
	 * 
	 * @param c
	 *            Bean类型
	 * @param where
	 *            条件表达式
	 * @param orderBy
	 *            排序
	 * @return
	 * @throws SQLException
	 */
	public <T> T selectSingle(Class<T> c, Expression where, OrderBy orderBy) throws SQLException {
		String sql = pingSql(c, where, new SqlCtrl(null, orderBy, new Limit(1)));
		if (null != where) {
			return parseObject(c, selectSingle(sql, where.getParams()));
		} else {
			return parseObject(c, selectSingle(sql));
		}
	}

	/**
	 * 返回一条记录
	 * 
	 * @param sql
	 *            查询语句
	 * @return
	 * @throws SQLException
	 */
	public ResultMap selectSingle(String sql) throws SQLException {
		Connection con = null;
		try {
			con = dbPool.getConnection();
			return dbUtil.select(con, sql).peek();
		} catch (NewObjectException e) {
			throw new SQLException(e);
		} finally {
			dbPool.releaseConnection(con);
		}
	}

	/**
	 * 返回一条记录
	 * 
	 * @param sql
	 *            查询语句
	 * @param params
	 *            参数
	 * @return
	 * @throws SQLException
	 */
	public ResultMap selectSingle(String sql, Object[] params) throws SQLException {
		Connection con = null;
		try {
			con = dbPool.getConnection();
			return dbUtil.select(con, sql, params).peek();
		} catch (NewObjectException e) {
			throw new SQLException(e);
		} finally {
			dbPool.releaseConnection(con);
		}
	}

	/**
	 * 查询记录集
	 * 
	 * @param sql
	 *            查询语句
	 * @return
	 * @throws SQLException
	 */
	public ResultList select(String sql) throws SQLException {
		Connection con = null;
		try {
			con = dbPool.getConnection();
			return dbUtil.select(con, sql);
		} catch (NewObjectException e) {
			throw new SQLException(e);
		} finally {
			dbPool.releaseConnection(con);
		}
	}

	/**
	 * 查询记录集
	 * 
	 * @param sql
	 *            查询语句
	 * @param params
	 *            参数
	 * @return
	 * @throws SQLException
	 */
	public ResultList select(String sql, Object[] params) throws SQLException {
		Connection con = null;
		try {
			con = dbPool.getConnection();
			return dbUtil.select(con, sql, params);
		} catch (NewObjectException e) {
			throw new SQLException(e);
		} finally {
			dbPool.releaseConnection(con);
		}
	}

	public <T> List<T> select(Class<T> c) throws SQLException {
		String sql = pingSql(c, null, null);
		return select(c, sql);
	}

	public <T> List<T> select(Class<T> c, SqlCtrl sqlCtrl) throws SQLException {
		return select(c, null, sqlCtrl);
	}

	public <T> List<T> select(Class<T> c, Expression where) throws SQLException {
		return select(c, where, null);
	}

	/**
	 * 查询记录集对象，返回转换过后的数据对象<br/>
	 * 一定要保证数据库字段名称与对象中的私有属性同名（这有点违反Java对属性的命名规则，但是能提高性能）
	 * 
	 * @param 对象类型
	 * @param table
	 *            表名
	 * @param where
	 *            查询条件
	 * @param groupBy
	 *            分组
	 * @param orderBy
	 *            排序
	 * @param limit
	 *            子结果集
	 * @return
	 * @throws SQLException
	 */
	public <T> List<T> select(Class<T> c, Expression where, SqlCtrl sqlCtrl) throws SQLException {
		String sb = pingSql(c, where, sqlCtrl);
		if (null != where) {
			return select(c, sb.toString(), where.getParams());
		} else {
			return select(c, sb.toString());
		}
	}

	private String pingSql(Class<?> c, Expression where, SqlCtrl sqlCtrl) {
		TableDefined tableDefined = TableDefinedUtil.ask(c);
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT ");
		if (mode == MODE_FIELDS_FROM_CLASS) {
			sb.append(SqlFieldUtil.getFieldsString(c));
		} else {
			sb.append("*");
		}
		sb.append(" FROM ");
		if (!tableDefined.db().isEmpty()) {
			sb.append(SqlNameTranser.IT.toString(tableDefined.db())).append(".");
		}
		sb.append(SqlNameTranser.IT.toString(tableDefined.table()));
		if (null != where) {
			sb.append(" ").append(where.toWhereString());
		}
		if (null != sqlCtrl) {
			if (null != sqlCtrl.groupBy) {
				sb.append(" ").append(sqlCtrl.groupBy);
			}
			if (null != sqlCtrl.orderBy) {
				sb.append(" ").append(sqlCtrl.orderBy);
			}
			if (null != sqlCtrl.limit) {
				sb.append(" ").append(sqlCtrl.limit);
			}
		}
		return sb.toString();
	}

	public DbUtil getDbUtil() {
		return dbUtil;
	}

	public static class SqlCtrl {

		private GroupBy groupBy;
		private OrderBy orderBy;
		private Limit limit;

		SqlCtrl(GroupBy groupBy, OrderBy orderBy, Limit limit) {
			this.groupBy = groupBy;
			this.orderBy = orderBy;
			this.limit = limit;
		}

		SqlCtrl(GroupBy groupBy, Limit limit) {
			this.groupBy = groupBy;
			this.limit = limit;
		}

		SqlCtrl(OrderBy orderBy, Limit limit) {
			this.orderBy = orderBy;
			this.limit = limit;
		}

		SqlCtrl(GroupBy groupBy, OrderBy orderBy) {
			this.groupBy = groupBy;
			this.orderBy = orderBy;
		}

		SqlCtrl(GroupBy groupBy) {
			this.groupBy = groupBy;
		}

		SqlCtrl(OrderBy orderBy) {
			this.orderBy = orderBy;
		}

		SqlCtrl(Limit limit) {
			this.limit = limit;
		}

		public GroupBy getGroupBy() {
			return groupBy;
		}

		public OrderBy getOrderBy() {
			return orderBy;
		}

		public Limit getLimit() {
			return limit;
		}

	}

}
